package com.cta.service.core;

import org.joda.money.BigMoney;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;

import com.cta.entity.CallReport;
import com.cta.exception.InsertDataFail;
import com.cta.exception.MissingConfigException;
import com.cta.service.RecoveryLogService;
import com.cta.view.model.CallData;
import com.cta.view.model.ConfigModel;

public abstract class AbstractProcessor implements Processor {
	
	@Autowired
	protected TaskExecutor executor;

	private static final Logger log = LoggerFactory.getLogger(AbstractProcessor.class);

	/**
	 * lookup area code
	 * 
	 * @param data
	 * @return
	 */
	public abstract ConfigModel lookup(CallData data);

	/**
	 * validate length of dialed number, duration ... if return null ->
	 * validation ok
	 * 
	 * @param data
	 * @return
	 */
	public abstract String validate(CallData data, ConfigModel config);

	/**
	 * calculate discount and real paid money
	 * 
	 * @param data
	 * @return
	 */
	public abstract BigMoney caculate(CallData data, ConfigModel config) throws Exception;

	/**
	 * end step, you can save data to database and notice to end user
	 * 
	 * @param data
	 * @param config
	 */
	public abstract void endProccess(CallReport data) throws InsertDataFail;

	@Override
	public CallReport process(final CallData data) {
		ConfigModel configModel = lookup(data);
		if (configModel != null) {
			log.info(String.format("Found area code: %s - %s", configModel.getAreaCode(), configModel.getAreaName()));
			String error = validate(data, configModel);
			if (error == null) {
				try {
					BigMoney price = caculate(data, configModel);
					// convert to call report;
					if (price != null) {
						endProccess(createCallReport(data, price, configModel));
					}
				} catch (MissingConfigException e) {
					// should be recovery here
					executor.execute(new Runnable() {
						@Override
						public void run() {
							RecoveryLogService.log(1, data.getRawData()+"-"+ data.getPbxId(), "May be missing configuration");
						}
					});
					log.error(e.getMessage());

				} catch (InsertDataFail e) {
					executor.execute(new Runnable() {
						@Override
						public void run() {
							RecoveryLogService.log(2, data.getRawData()+"-"+ data.getPbxId() , "Can not insert or create new table in database");
						}
					});
					log.error(">>>>>>>>>>>>>>>>>>>>>>> Can not save data to database, please check", e);
				} catch (Exception e) {
					executor.execute(new Runnable() {
						@Override
						public void run() {
							RecoveryLogService.log(3, data.getRawData()+"-"+ data.getPbxId(), "System calculate price has problem");
						}
					});
					log.error(
							String.format(" >>>>>>>>>>>>>>>>>>>>>>> Calculate fail for area code: %s - %s",
									configModel.getAreaCode(), configModel.getAreaName()), e);
				}
			} else {
				log.error(String.format(" >>>>>>>>>>>>>>>>>>>>>>> Validate fail for dial number: %s - reason: %s",
						data.getDialedNumber(), error));
			}
		} else {
			log.error(String.format(">>>>>>>>>>>>>>>>>>>>>>> Can not found area code for dial number: %s",
					data.getDialedNumber()));
		}
		return null;
	}

	protected CallReport createCallReport(CallData data, BigMoney price, ConfigModel configModel) {
		CallReport callR = new CallReport();
		callR.setPrice(price);
		callR.setCallingNum(data.getCallingNumber());
		callR.setDialedNum(data.getDialedNumber());
		callR.setTrunkId(data.getTrunkId());
		callR.setEmployeeNum(data.getEmployeeCode());
		callR.setStartDate(data.getStartDate());
		callR.setEndDate(data.getEndDate());
		callR.setDuration(data.getDurationTime());
		callR.setStartTime(data.getStartTime());
		callR.setDurationStr(data.getDuration().toString(ConfigModel.durationFormatter));
		callR.setDestination(configModel.getAreaName());
		callR.setTelCo(configModel.getTelco());
		callR.setDiscount(data.getDisountType());
		callR.setPriceStr(configModel.formatMoney(price));
		callR.setData(data.getData());
		callR.setPbxNo(data.getPbxId());
		return callR;
	}
}
